From fbf9c4996c5d8e18060c3391cc3a7e6bf3e81dca Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 19 Jul 2007 16:59:48 +0100 Subject: [PATCH] [xend / libxen] Add support for labeling of virtual network interfaces. This patch adds labeling of virtual network interfaces to xend and makes this manageable through the Xen-API. It's a feature that is only usable if ACM is enabled in Xen and xend is used through the xen-api. A labeled virtual network interface will be plugged into a bridge where other domains with the same-labeled network interface are connected to, so that only same-colored domains can communicate with each other. The bridge should be connected to the outside world using VLAN for isolation, extending the isolation beyond the local machine. If a virtual machine is labeled with a VM label that only has one Simple Type Enforcement Type then it is not necessary to label the virtual network interface, but the color of the network interface is determined from the VM's label. If, however, a virtual machine is labeled with a VM label that has multiple Simple Type Enforcement Types, then the explicit labeling of each virtual network interface is required. To specify the label of a network interface, the vif line in the VM's configuration file has been extended with parameters similar use for specifying the label of the VM: vif = ['policy=,label='] This labels the VIF of the virtual machine for usage under the policy 'policy name' and labels it with the label 'resource label'. Signed-off-by: Stefan Berger --- tools/libxen/include/xen/api/xen_vif.h | 14 +++++++ tools/libxen/src/xen_vif.c | 39 ++++++++++++++++++ tools/python/xen/util/security.py | 48 ++++++++++++++++------ tools/python/xen/xend/XendAPI.py | 19 +++++++++ tools/python/xen/xend/XendConfig.py | 10 +++++ tools/python/xen/xend/XendDomain.py | 23 +++++++++++ tools/python/xen/xend/XendDomainInfo.py | 2 + tools/python/xen/xend/XendXSPolicyAdmin.py | 12 ++++++ tools/python/xen/xend/server/netif.py | 43 +++++++++++++++++-- tools/python/xen/xm/addlabel.py | 41 ++++++++++++++++++ tools/python/xen/xm/create.dtd | 3 +- tools/python/xen/xm/create.py | 3 +- tools/python/xen/xm/getlabel.py | 26 ++++++++++++ tools/python/xen/xm/rmlabel.py | 38 +++++++++++++++-- tools/python/xen/xm/xenapi_create.py | 13 +++++- 15 files changed, 312 insertions(+), 22 deletions(-) diff --git a/tools/libxen/include/xen/api/xen_vif.h b/tools/libxen/include/xen/api/xen_vif.h index 26608f0af9..3fb8d7128e 100644 --- a/tools/libxen/include/xen/api/xen_vif.h +++ b/tools/libxen/include/xen/api/xen_vif.h @@ -362,4 +362,18 @@ extern bool xen_vif_get_all(xen_session *session, struct xen_vif_set **result); +/** + * Set the security label of a VIF. + */ +extern bool +xen_vif_set_security_label(xen_session *session, int64_t *result, xen_vif vif, + char *label, char *oldlabel); + + +/** + * Get the security label of a VIF. + */ +extern bool +xen_vif_get_security_label(xen_session *session, char **result, xen_vif vif); + #endif diff --git a/tools/libxen/src/xen_vif.c b/tools/libxen/src/xen_vif.c index bc9dd0dd2f..ac6147ff4f 100644 --- a/tools/libxen/src/xen_vif.c +++ b/tools/libxen/src/xen_vif.c @@ -575,3 +575,42 @@ xen_vif_get_uuid(xen_session *session, char **result, xen_vif vif) XEN_CALL_("VIF.get_uuid"); return session->ok; } + + +bool +xen_vif_set_security_label(xen_session *session, int64_t *result, xen_vif vif, + char *label, char *oldlabel) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = label }, + { .type = &abstract_type_string, + .u.string_val = oldlabel }, + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("VIF.set_security_label"); + return session->ok; +} + + +bool +xen_vif_get_security_label(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_security_label"); + return session->ok; +} diff --git a/tools/python/xen/util/security.py b/tools/python/xen/util/security.py index 608ed463f2..5183ed2c98 100644 --- a/tools/python/xen/util/security.py +++ b/tools/python/xen/util/security.py @@ -831,7 +831,7 @@ def get_domain_resources(dominfo): Entries are strored in the following formats: tap:qcow:/path/xyz.qcow """ - resources = { 'vbd' : [], 'tap' : []} + resources = { 'vbd' : [], 'tap' : [], 'vif' : []} devs = dominfo.info['devices'] uuids = devs.keys() for uuid in uuids: @@ -839,6 +839,15 @@ def get_domain_resources(dominfo): typ = dev[0] if typ in [ 'vbd', 'tap' ]: resources[typ].append(dev[1]['uname']) + if typ in [ 'vif' ]: + sec_lab = dev[1].get('security_label') + if sec_lab: + resources[typ].append(sec_lab) + else: + resources[typ].append("%s:%s:%s" % + (xsconstants.ACM_POLICY_ID, + active_policy, + "unlabeled")) return resources @@ -874,23 +883,36 @@ def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel, dictionary of the resource name to resource label mappings under which the evaluation should be done. """ + def collect_labels(reslabels, s_label, polname): + if len(s_label) != 3 or polname != s_label[1]: + return False + label = s_label[2] + if not label in reslabels: + reslabels.append(label) + return True + resources = get_domain_resources(dominfo) reslabels = [] # all resource labels + polname = xspol.get_name() - for key in resources.keys(): - for res in resources[key]: - try: - tmp = access_control[res] - if len(tmp) != 3: + for key, value in resources.items(): + if key in [ 'vbd', 'tap' ]: + for res in resources[key]: + try: + label = access_control[res] + if not collect_labels(reslabels, label, polname): + return False + except: return False - - if polname != tmp[1]: + elif key in [ 'vif' ]: + for xapi_label in value: + label = xapi_label.split(":") + if not collect_labels(reslabels, label, polname): return False - label = tmp[2] - if not label in reslabels: - reslabels.append(label) - except: - return False + else: + log.error("Unhandled device type: %s" % key) + return False + # Check that all resource labes have a common STE type with the # vmlabel rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels) diff --git a/tools/python/xen/xend/XendAPI.py b/tools/python/xen/xend/XendAPI.py index 3d890e7c44..2192d334b5 100644 --- a/tools/python/xen/xend/XendAPI.py +++ b/tools/python/xen/xend/XendAPI.py @@ -2084,6 +2084,25 @@ class XendAPI(object): def VIF_get_security_label(self, session, vif_ref): return self._VIF_get(vif_ref, 'security_label') + def _VIF_set(self, ref, prop, val, old_val): + return XendDomain.instance().set_dev_property_by_uuid( + 'vif', ref, prop, val, old_val) + + def VIF_set_security_label(self, session, vif_ref, sec_lab, old_lab): + xendom = XendDomain.instance() + dom = xendom.get_vm_with_dev_uuid('vif', vif_ref) + if not dom: + return xen_api_error(['HANDLE_INVALID', 'VIF', vif_ref]) + + if dom._stateGet() == XEN_API_VM_POWER_STATE_RUNNING: + raise SecurityError(-xsconstants.XSERR_RESOURCE_IN_USE) + + rc = self._VIF_set(vif_ref, 'security_label', sec_lab, old_lab) + if rc == False: + raise SecurityError(-xsconstants.XSERR_BAD_LABEL) + return xen_api_success(xsconstants.XSERR_SUCCESS) + + # Xen API: Class VIF_metrics # ---------------------------------------------------------------- diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py index 4dac85061b..5bc40a5dfb 100644 --- a/tools/python/xen/xend/XendConfig.py +++ b/tools/python/xen/xend/XendConfig.py @@ -1085,6 +1085,12 @@ class XendConfig(dict): self.device_duplicate_check(dev_type, dev_info, target) + if dev_type == 'vif': + if dev_info.get('policy') and dev_info.get('label'): + dev_info['security_label'] = "%s:%s:%s" % \ + (xsconstants.ACM_POLICY_ID, + dev_info['policy'],dev_info['label']) + # create uuid if it doesn't exist dev_uuid = dev_info.get('uuid', None) if not dev_uuid: @@ -1159,6 +1165,10 @@ class XendConfig(dict): network = XendAPIStore.get( cfg_xenapi.get('network'), 'network') dev_info['bridge'] = network.get_name_label() + + if cfg_xenapi.get('security_label'): + dev_info['security_label'] = \ + cfg_xenapi.get('security_label') dev_uuid = cfg_xenapi.get('uuid', None) if not dev_uuid: diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index c951dc6af8..3d48365571 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -688,6 +688,29 @@ class XendDomain: return value + def set_dev_property_by_uuid(self, klass, dev_uuid, field, value, + old_val = None): + rc = True + self.domains_lock.acquire() + + try: + try: + dom = self.get_vm_with_dev_uuid(klass, dev_uuid) + if dom: + o_val = dom.get_dev_property(klass, dev_uuid, field) + log.info("o_val=%s, old_val=%s" % (o_val, old_val)) + if old_val and old_val != o_val: + return False + + dom.set_dev_property(klass, dev_uuid, field, value) + self.managed_config_save(dom) + except ValueError, e: + pass + finally: + self.domains_lock.release() + + return rc + def is_valid_vm(self, vm_ref): return (self.get_vm_by_uuid(vm_ref) != None) diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index a69211d32b..94a02049a6 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -2420,6 +2420,8 @@ class XendDomainInfo: config['io_read_kbs'] = 0.0 config['io_write_kbs'] = 0.0 + config['security_label'] = config.get('security_label', '') + if dev_class == 'vbd': if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,): diff --git a/tools/python/xen/xend/XendXSPolicyAdmin.py b/tools/python/xen/xend/XendXSPolicyAdmin.py index 727cae664a..b3d7c2a9f1 100644 --- a/tools/python/xen/xend/XendXSPolicyAdmin.py +++ b/tools/python/xen/xend/XendXSPolicyAdmin.py @@ -312,6 +312,18 @@ class XSPolicyAdmin: vmlabel = pol.policy_get_domain_label_by_ssidref_formatted(ssidref) return vmlabel + def get_stes_of_vmlabel(self, vmlabel_xapi): + """ Get the list of STEs given a VM label in XenAPI format """ + stes = [] + loadedpol = self.get_loaded_policy() + if loadedpol: + tmp = vmlabel_xapi.split(":") + if len(tmp) != 3: + return [] + stes = loadedpol.policy_get_stes_of_vmlabel(tmp[2]) + return stes + + poladmin = None def XSPolicyAdminInstance(maxpolicies=1): diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py index 130668428c..3d4b598b91 100644 --- a/tools/python/xen/xend/server/netif.py +++ b/tools/python/xen/xend/server/netif.py @@ -26,6 +26,11 @@ import re from xen.xend import XendOptions from xen.xend.server.DevController import DevController +from xen.xend.XendError import VmError +from xen.util import security +from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + +from xen.xend.XendLogging import log xoptions = XendOptions.instance() @@ -108,6 +113,7 @@ class NetifController(DevController): ipaddr = config.get('ip') model = config.get('model') accel = config.get('accel') + sec_lab = config.get('security_label') if not typ: typ = xoptions.netback_type @@ -134,6 +140,8 @@ class NetifController(DevController): back['model'] = model if accel: back['accel'] = accel + if sec_lab: + back['security_label'] = sec_lab config_path = "device/%s/%d/" % (self.deviceClass, devid) for x in back: @@ -149,9 +157,34 @@ class NetifController(DevController): front = { 'handle' : "%i" % devid, 'mac' : mac } + if security.on(): + self.do_access_control(config) + return (devid, back, front) + def do_access_control(self, config): + """ do access control checking. Throws a VMError if access is denied """ + domain_label = self.vm.get_security_label() + stes = XSPolicyAdminInstance().get_stes_of_vmlabel(domain_label) + res_label = config.get('security_label') + if len(stes) > 1 or res_label: + if not res_label: + raise VmError("'VIF' must be labeled") + (label, ssidref, policy) = \ + security.security_label_to_details(res_label) + if domain_label: + rc = security.res_security_check_xapi(label, ssidref, + policy, + domain_label) + if rc == 0: + raise VmError("VM's access to network device denied. " + "Check labeling") + else: + raise VmError("VM must have a security label to access " + "network device") + + def getDeviceConfiguration(self, devid): """@see DevController.configuration""" @@ -160,10 +193,12 @@ class NetifController(DevController): config_path = "device/%s/%d/" % (self.deviceClass, devid) devinfo = () for x in ( 'script', 'ip', 'bridge', 'mac', - 'type', 'vifname', 'rate', 'uuid', 'model', 'accel'): + 'type', 'vifname', 'rate', 'uuid', 'model', 'accel', + 'security_label'): y = self.vm._readVm(config_path + x) devinfo += (y,) - (script, ip, bridge, mac, typ, vifname, rate, uuid, model, accel) = devinfo + (script, ip, bridge, mac, typ, vifname, rate, uuid, + model, accel, security_label) = devinfo if script: result['script'] = script @@ -185,5 +220,7 @@ class NetifController(DevController): result['model'] = model if accel: result['accel'] = accel - + if security_label: + result['security_label'] = security_label + return result diff --git a/tools/python/xen/xm/addlabel.py b/tools/python/xen/xm/addlabel.py index 9e93641284..bb27d30331 100644 --- a/tools/python/xen/xm/addlabel.py +++ b/tools/python/xen/xm/addlabel.py @@ -34,6 +34,7 @@ def help(): Format: xm addlabel